const multer = require('@koa/multer');
const AWS = require('aws-sdk');
const Minio = require('minio');
const path = require('path');
const fs = require('fs').promises;
const config = require('../config');
const { logger } = require('../utils/logger');
const { AppError } = require('../middlewares/errorHandler');

class FileService {
  
  constructor() {
    this.storageType = config.storage.type; // 'local', 's3', or 'minio'
    this.uploadPath = config.storage.uploadPath;

    // Initialize storage clients based on type
    if (this.storageType === 's3') {
      this.s3 = new AWS.S3({
        accessKeyId: config.storage.s3.accessKeyId,
        secretAccessKey: config.storage.s3.secretAccessKey,
        region: config.storage.s3.region
      });
    } else if (this.storageType === 'minio') {
      this.minioClient = new Minio.Client({
        endPoint: config.storage.minio.endPoint,
        port: config.storage.minio.port,
        useSSL: config.storage.minio.useSSL,
        accessKey: config.storage.minio.accessKey,
        secretKey: config.storage.minio.secretKey
      });
    }

    // Initialize multer for file uploads
    this.upload = multer({
      storage: multer.diskStorage({
        destination: (req, file, cb) => {
          cb(null, this.uploadPath);
        },
        filename: (req, file, cb) => {
          const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
          cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
        }
      }),
      limits: {
        fileSize: config.storage.maxFileSize
      },
      fileFilter: (req, file, cb) => {
        if (!config.storage.allowedMimeTypes.includes(file.mimetype)) {
          cb(new AppError(400, 'File type not allowed'), false);
          return;
        }
        cb(null, true);
      }
    });
  }

  /**
   * 保存文件
   * @param {Object} file - 文件对象
   * @param {Object} options - 保存选项
   * @returns {Promise<Object>} - 保存后的文件信息
   */
  async saveFile(file, options = {}) {
    try {
      switch (this.storageType) {
        case 'local':
          return await this.saveLocal(file);
        case 's3':
          return await this.saveToS3(file, options);
        case 'minio':
          return await this.saveToMinio(file, options);
        default:
          throw new AppError(500, 'Invalid storage type');
      }
    } catch (error) {
      logger.error('File save error:', error);
      throw error;
    }
  }

  /**
   * 保存文件到本地
   * @param {Object} file - 文件对象
   * @returns {Promise<Object>} - 保存后的文件信息
   */
  async saveLocal(file) {
    const filePath = path.join(this.uploadPath, file.filename);
    await fs.rename(file.path, filePath);
    return {
      filename: file.filename,
      path: filePath,
      size: file.size,
      mimetype: file.mimetype
    };
  }

  /**
   * 保存文件到S3
   * @param {Object} file - 文件对象
   * @param {Object} options - 保存选项
   * @returns {Promise<Object>} - 保存后的文件信息
   */
  async saveToS3(file, options) {
    const params = {
      Bucket: config.storage.s3.bucket,
      Key: options.key || file.filename,
      Body: await fs.readFile(file.path),
      ContentType: file.mimetype,
      ACL: options.acl || 'private'
    };

    const result = await this.s3.upload(params).promise();
    await fs.unlink(file.path); // Clean up local file

    return {
      filename: file.filename,
      location: result.Location,
      key: result.Key,
      size: file.size,
      mimetype: file.mimetype
    };
  }

  /**
   * 保存文件到Minio
   * @param {Object} file - 文件对象
   * @param {Object} options - 保存选项
   * @returns {Promise<Object>} - 保存后的文件信息
   */
  async saveToMinio(file, options) {
    const bucket = config.storage.minio.bucket;
    const objectName = options.key || file.filename;

    // Ensure bucket exists
    const bucketExists = await this.minioClient.bucketExists(bucket);
    if (!bucketExists) {
      await this.minioClient.makeBucket(bucket);
    }

    await this.minioClient.fPutObject(
      bucket,
      objectName,
      file.path,
      {
        'Content-Type': file.mimetype,
        ...options.metadata
      }
    );

    await fs.unlink(file.path); // Clean up local file

    const location = `${config.storage.minio.endPoint}/${bucket}/${objectName}`;
    return {
      filename: file.filename,
      location,
      key: objectName,
      size: file.size,
      mimetype: file.mimetype
    };
  }

  /**
   * 获取文件
   * @param {string} fileKey - 文件键
   * @param {Object} options - 获取选项
   * @returns {Promise<Object>} - 获取后的文件信息
   */
  async getFile(fileKey, options = {}) {
    try {
      switch (this.storageType) {
        case 'local':
          return await this.getLocal(fileKey);
        case 's3':
          return await this.getFromS3(fileKey, options);
        case 'minio':
          return await this.getFromMinio(fileKey, options);
        default:
          throw new AppError(500, 'Invalid storage type');
      }
    } catch (error) {
      logger.error('File get error:', error);
      throw error;
    }
  }

  /**
   * 获取本地文件
   * @param {string} filename - 文件名
   * @returns {Promise<Object>} - 获取后的文件信息
   */
  async getLocal(filename) {
    const filePath = path.join(this.uploadPath, filename);
    const stats = await fs.stat(filePath);
    return {
      stream: fs.createReadStream(filePath),
      size: stats.size
    };
  }

  /**
   * 获取S3文件
   * @param {string} key - 文件键
   * @param {Object} options - 获取选项
   * @returns {Promise<Object>} - 获取后的文件信息
   */
  async getFromS3(key, options) {
    const params = {
      Bucket: config.storage.s3.bucket,
      Key: key
    };

    const result = await this.s3.getObject(params).promise();
    return {
      stream: result.Body,
      size: result.ContentLength,
      metadata: result.Metadata
    };
  }

  /**
   * 获取Minio文件
   * @param {string} objectName - 对象名
   * @param {Object} options - 获取选项
   * @returns {Promise<Object>} - 获取后的文件信息
   */
  async getFromMinio(objectName, options) {
    const bucket = config.storage.minio.bucket;
    const stream = await this.minioClient.getObject(bucket, objectName);
    const stat = await this.minioClient.statObject(bucket, objectName);
    
    return {
      stream,
      size: stat.size,
      metadata: stat.metaData
    };
  }

  /**
   * 删除文件
   * @param {string} fileKey - 文件键
   * @returns {Promise<boolean>} - 删除结果
   */
  async deleteFile(fileKey) {
    try {
      switch (this.storageType) {
        case 'local':
          return await this.deleteLocal(fileKey);
        case 's3':
          return await this.deleteFromS3(fileKey);
        case 'minio':
          return await this.deleteFromMinio(fileKey);
        default:
          throw new AppError(500, 'Invalid storage type');
      }
    } catch (error) {
      logger.error('File delete error:', error);
      throw error;
    }
  }

  /**
   * 删除本地文件
   * @param {string} filename - 文件名
   * @returns {Promise<boolean>} - 删除结果
   */
  async deleteLocal(filename) {
    const filePath = path.join(this.uploadPath, filename);
    await fs.unlink(filePath);
    return true;
  }

  /**
   * 删除S3文件
   * @param {string} key - 文件键
   * @returns {Promise<boolean>} - 删除结果
   */
  async deleteFromS3(key) {
    const params = {
      Bucket: config.storage.s3.bucket,
      Key: key
    };

    await this.s3.deleteObject(params).promise();
    return true;
  }

  /**
   * 删除Minio文件
   * @param {string} objectName - 对象名
   * @returns {Promise<boolean>} - 删除结果
   */
  async deleteFromMinio(objectName) {
    await this.minioClient.removeObject(config.storage.minio.bucket, objectName);
    return true;
  }

  /**
   * 列出文件
   * @param {string} prefix - 文件前缀
   * @returns {Promise<Array<Object>>} - 文件列表
   */
  async listFiles(prefix = '') {
    try {
      switch (this.storageType) {
        case 'local':
          return await this.listLocal(prefix);
        case 's3':
          return await this.listFromS3(prefix);
        case 'minio':
          return await this.listFromMinio(prefix);
        default:
          throw new AppError(500, 'Invalid storage type');
      }
    } catch (error) {
      logger.error('File list error:', error);
      throw error;
    }
  }

  /**
   * 列出本地文件
   * @param {string} prefix - 文件前缀
   * @returns {Promise<Array<Object>>} - 文件列表
   */
  async listLocal(prefix) {
    const files = await fs.readdir(this.uploadPath);
    return files
      .filter(file => file.startsWith(prefix))
      .map(async file => {
        const stats = await fs.stat(path.join(this.uploadPath, file));
        return {
          name: file,
          size: stats.size,
          lastModified: stats.mtime
        };
      });
  }

  /**
   * 列出S3文件
   * @param {string} prefix - 文件前缀
   * @returns {Promise<Array<Object>>} - 文件列表
   */
  async listFromS3(prefix) {
    const params = {
      Bucket: config.storage.s3.bucket,
      Prefix: prefix
    };

    const result = await this.s3.listObjectsV2(params).promise();
    return result.Contents.map(item => ({
      name: item.Key,
      size: item.Size,
      lastModified: item.LastModified
    }));
  }

  /**
   * 列出Minio文件
   * @param {string} prefix - 文件前缀
   * @returns {Promise<Array<Object>>} - 文件列表
   */
  async listFromMinio(prefix) {
    const stream = this.minioClient.listObjects(config.storage.minio.bucket, prefix, true);
    const files = [];

    return new Promise((resolve, reject) => {
      stream.on('data', obj => {
        files.push({
          name: obj.name,
          size: obj.size,
          lastModified: obj.lastModified
        });
      });

      stream.on('error', reject);
      stream.on('end', () => resolve(files));
    });
  }

  /**
   * 获取上传中间件
   * @returns {multer} - 上传中间件
   */
    getUploadMiddleware() {
    return this.upload;
  }
}

module.exports = new FileService(); 